package utils

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"strings"
	"time"
)

// MCPNameMapping represents the mapping between endpoint keys and MCP technical names
type MCPNameMapping map[string]string

// ToolMappingManager manages MCP tool name mappings with database persistence
type ToolMappingManager struct {
	managementAPIURL string
	httpClient       *http.Client
}

// NewToolMappingManager creates a new tool mapping manager
func NewToolMappingManager(managementAPIURL string) *ToolMappingManager {
	return &ToolMappingManager{
		managementAPIURL: managementAPIURL,
		httpClient: &http.Client{
			Timeout: 10 * time.Second,
		},
	}
}

// LoadMCPNames loads existing MCP name mappings from database
func (tmm *ToolMappingManager) LoadMCPNames(toolID string) (MCPNameMapping, error) {
	url := fmt.Sprintf("%s/api/v1/tools/%s/mcp-names", tmm.managementAPIURL, toolID)

	resp, err := tmm.httpClient.Get(url)
	if err != nil {
		log.Printf("[MAPPING] Failed to load MCP names for tool %s: %v", toolID, err)
		return make(MCPNameMapping), nil // Return empty map on error, don't fail the process
	}
	defer resp.Body.Close()

	if resp.StatusCode == 404 {
		// Tool not found, return empty mapping
		return make(MCPNameMapping), nil
	}

	if resp.StatusCode != 200 {
		log.Printf("[MAPPING] Failed to load MCP names, status: %d", resp.StatusCode)
		return make(MCPNameMapping), nil
	}

	var response struct {
		MCPToolNames MCPNameMapping `json:"mcp_tool_names"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		log.Printf("[MAPPING] Failed to decode MCP names response: %v", err)
		return make(MCPNameMapping), nil
	}

	log.Printf("[MAPPING] Loaded %d existing MCP name mappings for tool %s", len(response.MCPToolNames), toolID)
	return response.MCPToolNames, nil
}

// SaveMCPNames saves MCP name mappings to database
func (tmm *ToolMappingManager) SaveMCPNames(toolID string, mappings MCPNameMapping) error {
	url := fmt.Sprintf("%s/api/v1/tools/%s/mcp-names", tmm.managementAPIURL, toolID)

	jsonData, err := json.Marshal(mappings)
	if err != nil {
		return fmt.Errorf("failed to marshal MCP names: %v", err)
	}

	req, err := http.NewRequest("PUT", url, bytes.NewBuffer(jsonData))
	if err != nil {
		return fmt.Errorf("failed to create request: %v", err)
	}
	req.Header.Set("Content-Type", "application/json")

	resp, err := tmm.httpClient.Do(req)
	if err != nil {
		log.Printf("[MAPPING] Failed to save MCP names for tool %s: %v", toolID, err)
		return err // 返回错误，而不是忽略
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		// 读取响应体以获取更多错误信息
		body, _ := io.ReadAll(resp.Body)
		err := fmt.Errorf("failed to save MCP names, status: %d, response: %s", resp.StatusCode, string(body))
		log.Printf("[MAPPING] %v", err)
		return err // 返回错误，而不是忽略
	}

	log.Printf("[MAPPING] Successfully saved %d MCP name mappings for tool %s", len(mappings), toolID)
	return nil
}

// GenerateEndpointKey creates a consistent key for endpoint identification
func GenerateEndpointKey(method, path string) string {
	return fmt.Sprintf("%s_%s", strings.ToLower(method), path)
}

// GenerateConsistentToolNames generates MCP tool names with database persistence
func (tmm *ToolMappingManager) GenerateConsistentToolNames(
	toolID string,
	prefix string,
	apis []APIEndpoint,
) (map[string]string, error) {
	// Load existing mappings from database
	existingMappings, err := tmm.LoadMCPNames(toolID)
	if err != nil {
		log.Printf("[MAPPING] Error loading existing mappings: %v", err)
		existingMappings = make(MCPNameMapping)
	}

	// Track all generated names for uniqueness across the current generation
	allGeneratedNames := make(map[string]bool)
	newMappings := make(MCPNameMapping)

	// First, preserve existing mappings and mark their names as used
	for endpointKey, techName := range existingMappings {
		allGeneratedNames[techName] = true
		newMappings[endpointKey] = techName
	}

	// Generate names for new endpoints
	for _, api := range apis {
		endpointKey := GenerateEndpointKey(api.Method, api.Path)

		// Skip if we already have a mapping for this endpoint
		if _, exists := existingMappings[endpointKey]; exists {
			log.Printf("[MAPPING] Using existing mapping for %s: %s", endpointKey, existingMappings[endpointKey])
			continue
		}

		// Generate new name for this endpoint
		originalFullName := fmt.Sprintf("%s_%s_%s", prefix, strings.ToLower(api.Method), api.Path)
		smartName := smartTruncateToolName(prefix, strings.ToLower(api.Method), api.Path)
		finalName := ensureUniqueToolName(smartName, originalFullName, allGeneratedNames)

		// Mark this name as used and store the mapping
		allGeneratedNames[finalName] = true
		newMappings[endpointKey] = finalName

		log.Printf("[MAPPING] Generated new mapping for %s: %s", endpointKey, finalName)
	}

	// Save updated mappings to database
	if err := tmm.SaveMCPNames(toolID, newMappings); err != nil {
		log.Printf("[MAPPING] Warning: Failed to save mappings: %v", err)
		// Continue even if saving fails
	}

	return newMappings, nil
}
